﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using winAppGame.model;
using winAppGame.service;

namespace winAppGame.service
{
    /// <summary>
    /// 出牌规则验证器抽象基类
    /// author:zuowenjun
    /// </summary>
    public abstract class PaiRuleChecker
    {
        /// <summary>
        /// 出牌规则至少的牌张数
        /// </summary>
        public abstract int ForBaseCount { get; }

        /// <summary>
        /// 在至少的牌张数基础上，出牌规则允许的增量牌张数
        /// </summary>
        public abstract int ForIncrementCount { get; }

        /// <summary>
        /// 适用的出牌规则
        /// </summary>
        public abstract List<PaiRuleType.PaiRuleTypeItem> ForRuleTypes { get; }

        private List<PaiRuleType.PaiRuleTypeItem> _ForRuleTypes = null;
        /// <summary>
        /// 验证选择的牌
        /// </summary>
        /// <param name="selectedPais"></param>
        /// <returns></returns>
        public PaiRuleCheckResult Check(IEnumerable<Pai> selectedPais)
        {
            return Check(selectedPais, null, null);
        }
        /// <summary>
        /// 验证选择的牌
        /// </summary>
        /// <param name="selectedPais"></param>
        /// <param name="lastShowPais"></param>
        /// <param name="lastShowRuleType"></param>
        /// <returns></returns>
        public PaiRuleCheckResult Check(IEnumerable<Pai> selectedPais, IEnumerable<Pai> lastShowPais, PaiRuleType.PaiRuleTypeItem lastShowRuleType)
        {
            if (!selectedPais.HasItem())
            {
                return new PaiRuleCheckResult(false, "未选择任何一张牌!");
            }

            int selectedPaisCount = selectedPais.Count();
            if (selectedPaisCount < ForBaseCount)
            {
                return new PaiRuleCheckResult(false, $"出牌的张数不正确，至少需要{ForBaseCount}张!");
            }

            if (selectedPaisCount > ForBaseCount)
            {
                bool checkOk = false;
                if (ForIncrementCount > 0)
                {
                    int loopCount = ForBaseCount;
                    while (selectedPaisCount > loopCount)
                    {
                        loopCount += ForIncrementCount;
                    }

                    if (selectedPaisCount != loopCount)
                    {
                        return new PaiRuleCheckResult(false, $"出牌的张数不正确，至少需要{loopCount - ForIncrementCount}张 或 {loopCount}!");
                    }
                    else
                    {
                        checkOk = true;
                    }
                }

                if (!checkOk)
                {
                    return new PaiRuleCheckResult(false, $"出牌的张数不正确，至少需要{ForBaseCount}张!");
                }
            }

            var context = new PaiRuleCheckContext();
            context.LastShowRuleType = lastShowRuleType;

            var checkResult = DoCheckSelectedPais(selectedPais.ToList(), context);
            if (!checkResult.AllowShow || !lastShowPais.HasItem())
            {
                return checkResult;
            }

            if (selectedPais.Count() != lastShowPais.Count() && !((new[] { PaiRuleType.Max, PaiRuleType.Four }).Contains(ForRuleTypes[0])))
            {
                return new PaiRuleCheckResult(false, $"出牌的张数：{selectedPais.Count()}与上家出牌的张数:{lastShowPais.Count()}不相同!");
            }

            return DoCheckAllowShowPais(selectedPais.ToList(), lastShowPais.ToList(), context);
        }

        protected abstract PaiRuleCheckResult DoCheckSelectedPais(List<Pai> selectedPais, PaiRuleCheckContext checkContext);

        protected abstract PaiRuleCheckResult DoCheckAllowShowPais(List<Pai> selectedPais, List<Pai> lastShowPais, PaiRuleCheckContext checkContext);

        /// <summary>
        /// 自动选择合适的出牌（用于电脑方）
        /// </summary>
        /// <param name="groupPais"></param>
        /// <param name="lastShowPais"></param>
        /// <param name="lastShowRuleType"></param>
        /// <returns></returns>
        public virtual PaiRuleCheckResult AutoSelectPais(IDictionary<Pai, int> groupPais, IEnumerable<Pai> lastShowPais, PaiRuleType.PaiRuleTypeItem showRuleType)
        {
            return PaiRuleCheckResult.Failed("要不起！");
        }

        protected HashSet<Pai> ConvertToSet(IEnumerable<Pai> pais)
        {
            return pais.ToHashSet(new LambdaEqualityComparer<Pai>((x, y) => x == y));
        }

        protected Dictionary<Pai, int> ConvertToDictionary(IEnumerable<Pai> pais)
        {
            var groupDic = new Dictionary<Pai, int>();
            foreach (var pai in pais)
            {
                if (groupDic.ContainsKey(pai))
                {
                    groupDic[pai] += 1;
                }
                else
                {
                    groupDic[pai] = 1;
                }
            }
            return groupDic;
        }

        protected bool IsSerial(List<Pai> pais)
        {
            pais.Sort();
            int prePai = pais[0];
            for (int i = 1; i < pais.Count; i++)
            {
                if (prePai + 1 != pais[i])
                {
                    return false;
                }
                prePai = pais[i];
            }
            return true;
        }

        protected int ComparePais(List<Pai> selectedPais, List<Pai> lastShowPais, int paiCount)
        {
            var selectedGroupDic = ConvertToDictionary(selectedPais);
            var showGroupDic = ConvertToDictionary(lastShowPais);

            var selectedMinPai = selectedGroupDic.Where(p => p.Value == paiCount).Min(p => p.Key);
            var showMinPai = showGroupDic.Where(p => p.Value == paiCount).Min(p => p.Key);
            return selectedMinPai.CompareTo(showMinPai);
        }


        protected List<PaiRuleType.PaiRuleTypeItem> BuildPaiRuleTypeItems(params PaiRuleType.PaiRuleTypeItem[] ruleTypeItems)
        {
            if (_ForRuleTypes != null)
            {
                return _ForRuleTypes;
            }
            return new List<PaiRuleType.PaiRuleTypeItem>(ruleTypeItems);
        }
    }

}
